/*
Copyright 2014 Yury Kharlamov

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Generated Inversion-Of-Control container
https://code.google.com/p/gen-ioc/

* * *

Abstract State Machine implementation
*/
#pragma once

#include <vector>
#include <list>
#include <functional>
#include <boost/function_types/parameter_types.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/bimap.hpp>
#include <boost/any.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>


namespace state_machine
{

	typedef std::function<void(const boost::any& e)> Action;
	typedef std::function<void()> VoidAction;

template <class T>
class StateAction
{
public:
	StateAction(const boost::optional<T>& s, const Action& a) :m_state(s), m_action(a){}
	boost::optional<T> GetState() const{ return m_state; }
	Action GetAction() const { return m_action; }
private:
	boost::optional<T> m_state;
	Action m_action;
};

template <class L, class E>
struct GetAction
{
	static Action Get(const L l)
	{
		return
			[l](const boost::any& e)
		{
			l(boost::any_cast<const E&>(e));
		};
	}
};

template <class L>
struct GetAction<L, boost::mpl::void_>
{
	static Action Get(const L l)
	{
		return
			[l](const boost::any&)
		{
			return l();
		};
	}
};
 
template <class L>
class EventType
{
	typedef decltype(&L::operator()) Op;
	typedef boost::function_types::parameter_types<Op> Params;
public:
#if _MSC_VER<1800
	typedef decltype(boost::mpl::at_c<Params, 1>::type()) TypePtr;
	typedef typename boost::remove_pointer<TypePtr>::type Type;
#else
	typedef typename boost::mpl::at_c<Params, 1>::type Type;
#endif
};

template <class T>
class State;

template <typename T>
class Rule
{
public:
	template <class Container, class Type>
	static bool Contains(const Container& c, const Type& t)
	{
		return std::find(c.begin(), c.end(), t) != c.end();
	}

	Rule() :m_isAuto(true), m_isInverted(false){}
	bool IsTrue(const T& t, const boost::any& evt) const
	{
		return (m_fromStates.empty() || (Contains(m_fromStates, t) ^ m_isInverted))
			&& (!m_guard || m_guard(evt));
	}
	bool IsAuto() const { return m_isAuto; }
	void AddState(const T& s)
	{
		m_fromStates.push_back(s);
	}
	void SetToState(const T& s)
	{
		m_toState = s;
	}
	void AddCondition(const std::function<bool(const boost::any&)>& c, bool isAuto)
	{
		m_isAuto = isAuto;
		m_guard = c;
	}
	void SetAction(const Action& a)
	{
		m_action = a;
	}
	void DoAction(const boost::any& e) const
	{
		if (m_action)
			m_action(e);
	}
	std::vector<T> GetStates() const
	{
		return m_fromStates;
	}
	boost::optional<T> GetToState()const{ return m_toState; }
	Rule<T> operator >(const StateAction<T>& sa) const
	{
		Rule r = *this;
		r.SetAction(sa.GetAction());
		boost::optional<T> s = sa.GetState();
		if (s)
			r.SetToState(s.get());
		return r;
	}
	Rule operator >(const State<T>& s) const
	{
		if (s.IsAny())
			return *this;
		Rule r = *this;
		r.SetToState(s.GetValue());
		return r;
	}
	template<class L>
	Rule<T> operator >(const L& l) const
	{
		Rule<T> r(*this);
		r.SetAction(GetAction<L, EventType<L>::Type>::Get(l));
		return r;
	}
	Rule<int> Transform(const std::function<int(const T&)>& toInt) const
	{
		Rule<int> r;
		r.AddCondition(m_guard, m_isAuto);
		r.SetAction(m_action);
		if (m_isInverted)
			r.SetIsInverted();
		if (m_toState)
			r.SetToState(toInt(m_toState.get()));
		for each(const T& t in m_fromStates)
			r.AddState(toInt(t));
		return r;
	}
	void SetIsInverted() { m_isInverted = true; }
	Rule<T> operator ~() const
	{
		Rule<T> r(*this);
		r.SetIsInverted();
		return r;
	}
	/*Rule<T> operator |(const State<T>& s) const
	{
		return s | *this;
	}*/
private:
	std::function<bool(const boost::any& evt)> m_guard;
	bool m_isAuto, m_isInverted;
	boost::optional<T> m_toState;
	std::vector<T> m_fromStates;
	Action m_action;
};

namespace state_action_type
{
	enum Enum{OnBefore, OnAfter, OnLeave, Count};
}

template <class T>
struct OnStateAction
{
	OnStateAction(const T& state, const VoidAction& action, state_action_type::Enum order) :state(state), action(action), order(order){}
	OnStateAction<int> Transform(const std::function<int(const T&)>& toInt) const
	{
		return OnStateAction<int>(toInt(state), action, order);
	}
	const T state;
	const VoidAction action;
	const state_action_type::Enum order;
};

template <class T, class Unused = void>
class ASM;

class Error
{
public:
	Error(){}
	Error(const std::string& text) : m_text(text){}
	std::string Text() const { return m_text; }
private:
	std::string m_text;
};

template <class E>
class ASM<E, typename boost::enable_if<boost::is_enum<E>>::type>
{
public:
	ASM(E initial) : m_asm(initial) 
	{
		m_asm.SetOnBeforeTransition([this](int from, int to){ OnBeforeTransition(static_cast<E>(from), static_cast<E>(to)); });
		m_asm.SetOnAfterTransition([this](int from, int to){ OnAfterTransition(static_cast<E>(from), static_cast<E>(to)); });
	}
	E GetState(){ return static_cast<E>(m_asm.GetState()); }

	template <class RuleType>
	void AddRule(const RuleType& r)
	{
		m_asm.AddRule(r.Transform([this](E e){ return e; }));
	}

	template <typename EventType>
	void QueueEvent(const EventType& event)
	{
		m_asm.QueueEvent(event);
	}
	typedef std::function<void(E fromState, E targetState)> OnTransitionHandlerType;
	void SetOnBeforeTransition(const OnTransitionHandlerType& handler) { m_onBeforeTransition = handler; }
	void SetOnAfterTransition(const OnTransitionHandlerType& handler) { m_onAfterTransition = handler; }
protected:
	virtual void OnBeforeTransition(E fromState, E targetState)
	{
		if (m_onBeforeTransition)
			m_onBeforeTransition(fromState, targetState);
	}
	virtual void OnAfterTransition(E fromState, E targetState)
	{
		if (m_onAfterTransition)
			m_onAfterTransition(fromState, targetState);
	}
private:
	ASM<int> m_asm;
	OnTransitionHandlerType m_onBeforeTransition, m_onAfterTransition;
};

template <>
class ASM<int, void>
{
	typedef std::list<Rule<int>> RuleCollection;
	typedef boost::lock_guard<boost::mutex> LockType;
	typedef std::map<int, VoidAction> StateToActionMap;
public:
	ASM(int initial) :m_state(initial){}
	int GetState(){ return m_state; }
	void AddRule(const Rule<int>& r)
	{
		if (r.IsAuto())
			m_autoRules.push_back(r);
		else
			m_rules.push_back(r);
	}

	void AddRule(const OnStateAction<int>& onState) { 
		AddStateHandler(onState, m_onState[onState.order]); }

	typedef std::function<void(int fromState, int targetState)> OnTransitionHandlerType;
	void SetOnBeforeTransition(const OnTransitionHandlerType& handler) { m_onBeforeTransition = handler; }
	void SetOnAfterTransition(const OnTransitionHandlerType& handler) { m_onAfterTransition = handler; }

	template <typename EventType>
	void QueueEvent(const EventType& event)
	{
		{
			LockType lock(m_mutex);
			m_eventQueue.push_back(event);
			if (m_eventQueue.size() > 1)
				return;
		}

		ProcessEventQueue();
	}
private:
	template <class ActionType>
	void AddStateHandler(const ActionType& a, StateToActionMap& m)
	{
		if (m.find(a.state) != m.end())
			throw std::logic_error("More than one handler for the same state");
		m[a.state] = a.action;
	}

	void ProcessEventQueue()
	{
		for (;;)
		{
			const boost::any& someEvent = m_eventQueue.front();

			try
			{
				ProcessEvent(someEvent, m_rules);
				while (ProcessEvent(boost::any(), m_autoRules));
			}
			catch (const std::exception& e)
			{
				LockType lock(m_mutex);
				m_eventQueue.insert(std::next(m_eventQueue.begin()), Error(e.what()));
			}

			{
				LockType lock(m_mutex);
				m_eventQueue.pop_front();
				if (m_eventQueue.empty())
					break;
			}
		}
	}

	bool ProcessEvent(const boost::any& event, RuleCollection& rules)
	{
		const int oldState = m_state;
		for each(const Rule<int>& rule in rules)
		{
			if (rule.IsTrue(oldState, event))
			{
				const boost::optional<int> ruleToState = rule.GetToState();
				const int newState = ruleToState ? ruleToState.get() : oldState;
				if (oldState != newState)
				{
					OnBeforeTransition(oldState, newState);
					CallStateHandler(oldState, m_onState[state_action_type::OnLeave]);
					CallStateHandler(newState, m_onState[state_action_type::OnBefore]);
				}
				m_state = newState;
				rule.DoAction(event);
				if (oldState != newState)
				{
					CallStateHandler(newState, m_onState[state_action_type::OnAfter]);
					OnAfterTransition(oldState, newState);
				}
				return true;
			}
		}
		return false;
	}
	void CallStateHandler(int state, const StateToActionMap& handlers)
	{
		auto handler = handlers.find(state);
		if (handler != handlers.end())
			handler->second();
	}

	virtual void OnBeforeTransition(int fromState, int targetState)
	{
		if (m_onBeforeTransition)
			m_onBeforeTransition(fromState, targetState);
	}
	virtual void OnAfterTransition(int fromState, int targetState)
	{
		if (m_onAfterTransition)
			m_onAfterTransition(fromState, targetState);
	}
private:
	RuleCollection m_rules, m_autoRules;
	std::list<boost::any> m_eventQueue;
	int m_state;
	boost::mutex m_mutex;
	StateToActionMap m_onState[state_action_type::Count];
	OnTransitionHandlerType m_onBeforeTransition, m_onAfterTransition;
};

template <class T>
class ASM<T, typename boost::disable_if<boost::is_enum<T>>::type>
{
	typedef boost::bimap<T, int> Bimap;
public:
	ASM(const T& initial) : m_nextInt(0), m_asm(0)
	{ 
		AddToMap(initial); 
		m_asm.SetOnBeforeTransition([this](int from, int to){ OnBeforeTransitionInternal(from, to); });
		m_asm.SetOnAfterTransition([this](int from, int to){ OnAfterTransitionInternal(from, to); });
	}
	T GetState(){ return m_states.right.at(m_asm.GetState()); }

	template <class RuleType>
	void AddRule(const RuleType& r)
	{
		AddToMap(r);
		m_asm.AddRule(r.Transform([this](const T& t){return m_states.left.at(t); }));
	}
	template <typename EventType>
	void QueueEvent(const EventType& event)
	{
		m_asm.QueueEvent(event);
	}
	typedef std::function<void(const T& fromState, const T& targetState)> OnTransitionHandlerType;
	void SetOnBeforeTransition(const OnTransitionHandlerType& handler) { m_onBeforeTransition = handler; }
	void SetOnAfterTransition(const OnTransitionHandlerType& handler) { m_onAfterTransition = handler; }
protected:
	virtual void OnBeforeTransition(const T& fromState, const T& targetState)
	{
		if (m_onBeforeTransition)
			m_onBeforeTransition(fromState, targetState);
	}
	virtual void OnAfterTransition(const T& fromState, const T& targetState)
	{
		if (m_onAfterTransition)
			m_onAfterTransition(fromState, targetState);
	}

private:
	void OnBeforeTransitionInternal(int fromState, int targetState)
	{
		OnBeforeTransition(m_states.right.at(fromState), m_states.right.at(targetState));
	}
	void OnAfterTransitionInternal(int fromState, int targetState)
	{
		OnAfterTransition(m_states.right.at(fromState), m_states.right.at(targetState));
	}
	void AddToMap(const Rule<T>& r)
	{
		boost::optional<T> to = r.GetToState();
		if (to)
			AddToMap(to.get());
		for each(const T& s in r.GetStates())
			AddToMap(s);
	}

	void AddToMap(const OnStateAction<T>& r)
	{
		AddToMap(r.state);
	}

	void AddToMap(const T& t)
	{
		if (m_states.left.find(t) == m_states.left.end())
			m_states.insert(Bimap::value_type(t, m_nextInt++));
	}
	ASM<int, void> m_asm;
	Bimap m_states;
	int m_nextInt;
	OnTransitionHandlerType m_onBeforeTransition, m_onAfterTransition;
};

template <class T>
class RuleInserter
{
public:
	RuleInserter(ASM<T>& sm) : m_sm(sm){}

	template <class RuleType>
	RuleInserter<T> operator,(const RuleType& r)
	{
		m_sm.AddRule(r);
		return *this;
	}
private:
	ASM<T>& m_sm;
};

template <class T, class RuleType>
RuleInserter<T> operator +=(ASM<T>& sm, const RuleType& r)
{
	return RuleInserter<T>(sm), r;
}


template <class T>
struct Event{};

template <class T>
class State
{
public:


	State(const T& value):m_isInverted(false){ m_values.push_back(value); }
	static State Any() { return State(); }
	bool IsAny() const { return m_values.size() == 0; }
	T GetValue() const 
	{
		if (IsAny())
			throw std::logic_error("Cannot get value of Any state");
		if (m_values.size()>1)
			throw std::logic_error("Cannot get value of multiple states");
		return m_values[0]; 
	}

	OnStateAction<T> OnBefore(const VoidAction& action)
	{
		return OnStateAction<T>(GetValue(), action, state_action_type::OnBefore);
	}

	OnStateAction<T> OnAfter(const VoidAction& action)
	{
		return OnStateAction<T>(GetValue(), action, state_action_type::OnAfter);
	}

	OnStateAction<T> OnLeave(const VoidAction& action)
	{
		return OnStateAction<T>(GetValue(), action, state_action_type::OnLeave);
	}

	State operator|(const State& s) const
	{
		if (IsAny() || s.IsAny())
			throw std::logic_error("Cannot apply OR to any state");
		State newState;
		newState.m_values = m_values;
		newState.m_values.insert(newState.m_values.end(), s.m_values.begin(), s.m_values.end());
		newState.m_isInverted = m_isInverted || s.m_isInverted;
		return newState;
	}

	Rule<T> operator|(const Rule<T>& r) const
	{
		Rule<T> rule(r);
		for each(const T& t in m_values)
			rule.AddState(t);
		if (m_isInverted)
			rule.SetIsInverted();
		return rule;
	}

	template <class L>
	Rule<T> operator/(const L& l) const
	{
		Rule<T> rule;
		rule = *this | rule;

		AddCondition<L, EventType<L>::Type>::Add(l, rule);
		return rule;
	}
	
	template <class E>
	Rule<T> operator/(const Event<E>&) const
	{
		Rule<T> rule;
		rule = *this | rule;

		rule.AddCondition(
			[](const boost::any& e) -> bool
		{
			return e.type() == typeid(E);
		}, false);
		return rule;
	}


	template <class E>
	Rule<T> operator+(const E&) const
	{
		return (*this) / Event<E>();
	}

	Rule<T> operator >(const State<T>& s)
	{
		Rule<T> rule;
		rule = *this | rule;
		rule.SetToState(s.GetValue());
		return rule;
	}

	template <class L>
	StateAction<T> operator -(const L& l)
	{
		return StateAction<T>(GetValue(), GetAction<L, EventType<L>::Type>::Get(l));
	}

	template<class L>
	Rule<T> operator >(const L& l)
	{
		Rule<T> r;
		r = *this | r;
		r.SetAction(GetAction<L, EventType<L>::Type>::Get(l));
		return r;
	}
	State operator ~()
	{
		State s;
		s.m_values = m_values;
		s.m_isInverted = true;
		return s;
	}
private:
	State(){}

	template <class L, class E>
	struct AddCondition
	{
		static void Add(const L l, Rule<T>& r)
		{
			r.AddCondition(
				[l](const boost::any& e)
			{
				return e.type() == typeid(E) && l(boost::any_cast<const E&>(e));
			}
			, false);
		}
	};

	template <class L>
	struct AddCondition<L, boost::mpl::void_>
	{
		static void Add(const L l, Rule<T>& r)
		{
			r.AddCondition(
				[l](const boost::any&)
			{
				return l();
			}
			, true);
		}
	};

	std::vector<T> m_values;
	bool m_isInverted;
};

}